package com.umeox.moto.watch.themes.app;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.umeox.moto.watch.themes.R;
import com.umeox.moto.watch.themes.skin.loader.ResourceManager;
import com.umeox.watch.moto.dataservice.DataProvider;

import java.lang.ref.WeakReference;
import java.util.ArrayList;

public class ThemeManager {

    public static final int THEME_UNDEFINED = R.style.Theme_Dark;
    private boolean initialized = false;

    public Context getContext() {
        return hostContext;
    }

    private Context hostContext;
    private SparseArray<int[]> mStyles = new SparseArray<>();
    private int mCurrentTheme;
    private EventDispatcher mDispatcher;

    public static ThemeManager getInstance() {
        return InnerHolder.INSTANCE;
    }

    private void checkInitialize() {
        if (!this.initialized) {
            throw new RuntimeException("initialize first");
        }
    }

    public boolean isInitialized() {
        return initialized;
    }

    public void setup(Context context) {
        setup(context, 0, null);
    }

    public void setup(Context context, int defaultTheme, EventDispatcher dispatcher) {
        hostContext = context;
        mDispatcher = dispatcher != null ? dispatcher : new SimpleDispatcher();
        ResourceManager.getInstance().init(context);
        DataProvider.initialize(hostContext);
        initialized = true;
        int tempTheme = getCurrentTheme();
        int[] styles = getStyleList(context, R.array.themes);
        if (tempTheme < 0 || (styles != null && tempTheme >= styles.length)) {
            tempTheme = defaultTheme;
        }
        setCurrentTheme(tempTheme);
        ResourceManager.getInstance().load(tempTheme);
    }

    private int[] getStyleList(Context context, int styleId) {
        if (mStyles == null) {
            return null;
        }
        int[] list = mStyles.get(styleId);
        if (list == null) {
            list = loadStyleList(context, styleId);
            mStyles.put(styleId, list);
        }
        return list;
    }

    private int[] loadStyleList(Context context, int resId) {
        if (context == null) {
            return null;
        }
        TypedArray array = context.getResources().obtainTypedArray(resId);
        int[] result = new int[array.length()];
        for (int i = 0; i < result.length; i++) {
            result[i] = array.getResourceId(i, 0);
        }
        array.recycle();
        return result;
    }

    private void dispatchThemeChanged(int theme) {
        if (mDispatcher != null) {
            mDispatcher.dispatchThemeChanged(theme);
        }
    }

    public int getCurrentTheme() {
        int currentTheme;
        if (!TextUtils.isEmpty(getSharedUserId(hostContext)) && getSharedUserId(hostContext).equals("android.uid.system")) {
            currentTheme = Settings.Global.getInt(hostContext.getContentResolver(), "theme_style", 0);
        } else {
            checkInitialize();
            currentTheme = DataProvider.getCurrentTheme();
        }
        return currentTheme;
    }

    public boolean setCurrentTheme(int theme) {
        if (mCurrentTheme != theme) {
            try {
                if (DataProvider.setTheme(theme)) {
                    mCurrentTheme = theme;
                    Settings.Global.putInt(hostContext.getContentResolver(), "theme_style", mCurrentTheme);
                    dispatchThemeChanged(mCurrentTheme);
                    return true;
                }
            } catch (Exception e) {
                mCurrentTheme = theme;
                Settings.Global.putInt(hostContext.getContentResolver(), "theme_style", mCurrentTheme);
                dispatchThemeChanged(mCurrentTheme);
            }

        }
        return false;
    }

    public int getCurrentStyle(Context context) {

        return getStyle(context, R.array.themes, mCurrentTheme);
    }

    /**
     * Get current style of a styleId.
     *
     * @param styleId The styleId.
     * @return The current style.
     */
    public int getCurrentStyle(Context context, int styleId) {
        return getStyle(context, styleId, mCurrentTheme);
    }

    /**
     * Get a specific style of a styleId.
     *
     * @param styleId The styleId.
     * @param theme   The theme.
     * @return The specific style.
     */
    public int getStyle(Context context, int styleId, int theme) {
//        checkInitialize();
        int[] styles = getStyleList(context, styleId);
        if (styles != null && (theme >= styles.length || theme < 0)) {
            setCurrentTheme(0);
        }
        return styles == null ? R.style.Theme_Dark : styles[theme];
    }

    /**
     * Register a listener will be called when current theme changed.
     *
     * @param listener A {@link OnThemeChangedListener} will be registered.
     */
    public void registerOnThemeChangedListener(@NonNull OnThemeChangedListener listener) {
        checkInitialize();
        if (mDispatcher != null) {
            mDispatcher.registerListener(listener);
        }
    }

    /**
     * Unregister a listener from be called when current theme changed.
     *
     * @param listener A {@link OnThemeChangedListener} will be unregistered.
     */
    public void unregisterOnThemeChangedListener(@NonNull OnThemeChangedListener listener) {
        checkInitialize();
        if (mDispatcher != null) {
            mDispatcher.unregisterListener(listener);
        }
    }

    public void initSkin() {
        int currentTheme;
        if (!TextUtils.isEmpty(getSharedUserId(hostContext)) && getSharedUserId(hostContext).equals("android.uid.system")) {
            currentTheme = Settings.Global.getInt(hostContext.getContentResolver(), "theme_style", 0);
        } else {
            currentTheme = DataProvider.getCurrentTheme();
        }
        if (mCurrentTheme != currentTheme) {
            mCurrentTheme = currentTheme;
            ResourceManager.getInstance().load(mCurrentTheme);
            dispatchThemeChanged(mCurrentTheme);
        }
    }

    public interface EventDispatcher {
        void registerListener(OnThemeChangedListener listener);

        void unregisterListener(OnThemeChangedListener listener);

        void dispatchThemeChanged(int theme);
    }

    public interface OnThemeChangedListener {
        void onThemeChanged(@Nullable OnThemeChangedEvent event);
    }

    public static class SimpleDispatcher implements EventDispatcher {
        ArrayList<WeakReference<OnThemeChangedListener>> mListeners = new ArrayList<>();

        @Override
        public void registerListener(OnThemeChangedListener listener) {
            boolean exist = false;
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                WeakReference<OnThemeChangedListener> ref = mListeners.get(i);
                if (ref.get() == null) {
                    mListeners.remove(i);
                } else if (ref.get() == listener) {
                    exist = true;
                }
            }

            if (!exist) {
                mListeners.add(new WeakReference<>(listener));
            }
        }

        @Override
        public void unregisterListener(OnThemeChangedListener listener) {
            for (int i = mListeners.size() - 1; i >= 0; i--) {
                WeakReference<OnThemeChangedListener> ref = mListeners.get(i);
                if (ref.get() == null || ref.get() == listener) {
                    mListeners.remove(i);
                }
            }
        }

        @Override
        public void dispatchThemeChanged(int theme) {
            OnThemeChangedEvent event = new OnThemeChangedEvent(theme);

            for (int i = mListeners.size() - 1; i >= 0; i--) {
                WeakReference<OnThemeChangedListener> ref = mListeners.get(i);
                if (ref.get() == null) {
                    mListeners.remove(i);
                } else {
                    ref.get().onThemeChanged(event);
                }
            }
        }
    }

    public static class OnThemeChangedEvent {
        public final int theme;

        public OnThemeChangedEvent(int theme) {
            this.theme = theme;
        }
    }

    private static class InnerHolder {
        private static ThemeManager INSTANCE = new ThemeManager();
    }

    private String getSharedUserId(Context context) {
        String appSharedUserId = null;
        try {
            PackageInfo packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            appSharedUserId = packageInfo.sharedUserId;
            Log.e("sharedUserId", "sharedUserId=" + appSharedUserId);
        } catch (
                PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return appSharedUserId;
    }


}
